// import { loadRefreshToken } from '@/services/user'
import { Modal } from 'ant-design-vue'
import { WHITE_API } from '@/services/api'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import { axios } from '@/utils/request'
import { isJsonString } from '@/utils/util'
import Qs from 'qs'
import Axios from 'axios'
// import Vue from 'vue'

// 请求队列
const requestQueue = {
  // 请求列表，存储刷新 token 期间发起的请求
  list: [],
  isRefreshing: false,
  // 添加请求
  add(config, resolve, reject, type) {
    this.list.push((newToken) => {
      if (newToken) {
        config.headers[ACCESS_TOKEN] = newToken
        // type标记是否需要重新发起请求，待token刷新后，继续请求即可，不带该属性token刷新后需要重新请求
        type === 'request' ? resolve(config) : resolve(axios(config))
      } else { // token不为真 表示 token 获取失败，清理队列请求的 Promise，避免内存堆积
        reject({ status: 200, data: { code: 401 }, message: 'token已失效,请重新登录' })
      }
    })
  },
  // 执行队列
  execute(token) {
    let fn
    // eslint-disable-next-line
    while (fn = this.list.shift()) {
      fn(token)
    }
  },
  // 清空队列
  clear() {
    this.execute()
  }
}

function directLogin() { // 退出重新登录
  try {
    const path = window.document.location.pathname
    if (path !== '/' && path.indexOf('/login') === -1) {
      window.location.reload()
    }
  } catch (e) {
    window.location.reload()
  }
}

function loginExpired(options) { // 两个token全部超时
  setTimeout(() => {
    const path = window.document.location.pathname
    if (path !== '/' && path.indexOf('/login') === -1) {
      Modal.error({
        title: '登录已过期',
        content: '很抱歉，登录已过期，请重新登录',
        okText: '重新登录',
        mask: false,
        onOk: () => {
          const { store } = options
          store.dispatch('account/Logout').then(() => {
            directLogin()
          })
        }
      })
    }
  }, 200)
}

function handleByRefreshStatus(config, options, type = 'response') {
  return new Promise((resolve, reject) => {
    const status = requestQueue.refreshTokenStatus
    if (type === 'request') {
      if (!status) { // 判断刷新状态
        resolve(config) // 刷新状态为否时，直接返回继续请求
      } else {
        config._retry = true// token刷新后标识,根据该标识区分是否是再次刷新的请求
        requestQueue.add(config, resolve, reject, type)// 刷新状态为true时，暂存请求，待token刷新后继续请求
      }
    } else {
      config._retry = true// token刷新后标识继续请求即可。与response类型区分
      requestQueue.add(config, resolve, reject)
      getNewToken(options)
    }
  })
}

async function getNewToken(options) { // 获取新token
  try {
    if (requestQueue.refreshTokenStatus) {
      return false
    }
    const { store } = options
    requestQueue.refreshTokenStatus = true
    const res = await store.dispatch('account/RefreshToken')
    requestQueue.refreshTokenStatus = false
    if (res.code === 200) {
      requestQueue.execute(res.result.token)
    } else {
      requestQueue.clear()
      loginExpired(options)
    }
  } catch (error) {
    requestQueue.refreshTokenStatus = false
    requestQueue.clear()
    loginExpired(options)
  }
}

/**
 * 防止重复请求-begin
 * 同时发起多个请求时，取消除第一次之外的其他请求
 */
const pendingRequest = new Map()

function generateReqKey(config) {
  // 响应的时候，response.config 中的data 是一个JSON字符串，所以需要转换一下
  if (config && config.data && isJsonString(config.data)) {
    config.data = JSON.parse(config.data)
  }
  const { method, url, params, data } = config
  return [method, url, Qs.stringify(params), Qs.stringify(data)].join('&')
}

function addPendingRequest(config) {
  // 如果配置了取消请求，就去判断
  if (config.cancelRequest === true) {
  // 生成请求的标识编码
    const requestKey = generateReqKey(config)
    if (pendingRequest.has(requestKey)) {
    // 如果存在，则在添加时就直接取消请求
      config.cancelToken = new Axios.CancelToken((cancel) => {
      // cancel 函数的参数会作为 promise 的 error 被捕获
        cancel(`已取消的重复请求：${config.url}`)
      })
    } else {
    // 第一个是如果重复了2次以上，后面的就都用第一次的token
    // 否则就新建一个token
      config.cancelToken =
        config.cancelToken ||
        new Axios.CancelToken((cancel) => {
          pendingRequest.set(requestKey, cancel)
        })
    }
  }
}

function removePendingRequest(config) {
  const requestKey = generateReqKey(config)
  if (pendingRequest.has(requestKey)) {
    const cancel = pendingRequest.get(requestKey)
    cancel(requestKey)
    // 删除防止重复请求标记cancelToken，避免后续根据config再次请求报错
    delete config.cancelToken
    pendingRequest.delete(requestKey)
  }
}
// 防止重复请求-end

const respError = {
  onFulfilled(response, options) {
    const { message } = options
    const { config } = response
    removePendingRequest(config) // 从pendingRequest对象中移除请求
    if (response.data && response.data.code !== 200) {
      // 不需要统一处理提示的可以设置_message为false
      config._message !== false && message.error(response.data.message)
    }
    return response
  },
  onRejected(error, options) {
    const { response } = error
    removePendingRequest(error.config || {}) // 从pendingRequest对象中移除请求
    if (Axios.isCancel(error)) {
      console.log(`已取消的重复请求：${error.message}`)
    }
    const { message } = options
    // 处理异常状态，401状态后续有单独方法处理
    if (response && response.status !== 200 && response.status !== 401) {
      message.error(response.data.message || response.message)
    }
    return Promise.reject(error)
  }
}

const resp200 = {
  async onFulfilled(response, options) {
    const { config } = response

    // 若为重新请求的接口，直接返回response
    if (config._retry) {
      delete config._retry
      return response
    }

    // 请求成功或refresh_token请求
    if (response.data && response.data.code === 200) {
      // _allResponse为true,返回包含请求头、响应头内的全部数据，默认只返回响应数据
      return config._allResponse ? response : response.data
    }

    return config._allResponse ? response : response.data
  },
  onRejected(error, options) {
    return Promise.reject(error)
  }
}

const resp401 = {
  onFulfilled(response, options) {
    // 处理status状态为200时。result对象code为401状态
    if (response.data && response.data.code === 401) {
      return handleByRefreshStatus(response.config, options)
    }
    return response
  },
  onRejected(error, options) {
    const { response } = error
    // 处理token过期
    if (response && response.status === 401) {
      return handleByRefreshStatus(response.config, options)
    }
    return Promise.reject(error)
  }
}

const reqCommon = {
  /**
   * 发送请求之前做些什么
   * @param config axios config
   * @param options 应用配置 包含: {router, i18n, store, message}
   * @returns {*}
   */
  onFulfilled(config, options) {
    const { store, router } = options
    const token = store.getters.token
    config.params = {
      _key: router.currentRoute?.meta?.key,
      ...config.params
    }

    // 如果 token 存在
    // 让每个请求携带自定义 token 请根据实际情况自行修改
    if (token) {
      config.headers['Access-Token'] = token
    }

    // pending 中的请求，后续请求不发送（由于存放的pedingMap 的key 和参数有关，所以放在参数处理之后）
    addPendingRequest(config) // 把当前请求信息添加到pendingRequest对象中

    // 白名单不验证token接口直接请求
    if (WHITE_API.includes(config.url)) {
      return config
    }

    // 需要验证token接口
    return handleByRefreshStatus(config, options, 'request')
  },
  /**
   * 请求出错时做点什么
   * @param error 错误对象
   * @param options 应用配置 包含: {router, i18n, store, message}
   * @returns {Promise<never>}
   */
  onRejected(error, options) {
    const { message } = options
    message.error(error.message)
    return Promise.reject(error)
  }
}

export default {
  request: [reqCommon], // 请求拦截
  response: [respError, resp401, resp200] // 响应拦截
}
